﻿using OpenTK;
using OpenTK.Graphics.OpenGL;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LDLib
{
    /// <summary>
    /// Material class. Keeps track of the shader
    /// and the values used for the shader.
    /// </summary>
    public class Material
    {
        /// <summary>
        /// Name of this material
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Shader used for this material
        /// </summary>
        public Shader Shader { get; set; }

        /// <summary>
        /// Vector4 values
        /// </summary>
        private Dictionary<string, Vector4> vec4Values = new Dictionary<string, Vector4>();

        /// <summary>
        /// Vector3 values
        /// </summary>
        private Dictionary<string, Vector3> vec3Values = new Dictionary<string, Vector3>();

        /// <summary>
        /// Matrix4 values
        /// </summary>
        private Dictionary<string, Matrix4> mat4Values = new Dictionary<string, Matrix4>();
        
        /// <summary>
        /// Int values
        /// </summary>
        private Dictionary<string, int> intValues = new Dictionary<string, int>();
        
        /// <summary>
        /// Float values
        /// </summary>
        private Dictionary<string, float> floatValues = new Dictionary<string, float>();

        /// <summary>
        /// Texture values
        /// </summary>
        private Dictionary<string, int> textureValues = new Dictionary<string, int>();

        /// <summary>
        /// Constructs a new material
        /// </summary>
        /// <param name="name">Name of the material</param>
        public Material(string name)
        {
            this.Name = name;
        }

        /// <summary>
        /// Constructs a new material
        /// </summary>
        /// <param name="name">Name of the material</param>
        /// <param name="shader">Shader used for the material</param>
        public Material(string name, Shader shader)
        {
            this.Name = name;
            this.Shader = shader;
        }

        public void Set(string name, Vector4 v)
        {
            if (!vec4Values.ContainsKey(name)) vec4Values.Add(name, v);
            else vec4Values[name] = v;
        }

        /// <summary>
        /// Sets a Vector3 value
        /// </summary>
        /// <param name="name">Name of the value (in the shader)</param>
        /// <param name="v">Value</param>
        public void Set(string name, Vector3 v)
        {
            if (!vec3Values.ContainsKey(name)) vec3Values.Add(name, v);
            else vec3Values[name] = v;
        }

        /// <summary>
        /// Sets a Matrix4 value
        /// </summary>
        /// <param name="name">Name of the value (in the shader)</param>
        /// <param name="m">Value</param>
        public void Set(string name, Matrix4 m)
        {
            if (!mat4Values.ContainsKey(name)) mat4Values.Add(name, m);
            else mat4Values[name] = m;
        }

        /// <summary>
        /// Sets an int value
        /// </summary>
        /// <param name="name">Name of the value (in the shader)</param>
        /// <param name="i">Value</param>
        public void Set(string name, int i)
        {
            if (!intValues.ContainsKey(name)) intValues.Add(name, i);
            else intValues[name] = i;
        }

        /// <summary>
        /// Sets a float value
        /// </summary>
        /// <param name="name">Name of the value (in the shader)</param>
        /// <param name="f">Value</param>
        public void Set(string name, float f)
        {
            if (!floatValues.ContainsKey(name)) floatValues.Add(name, f);
            else floatValues[name] = f;
        }

        /// <summary>
        /// Sets a texture
        /// </summary>
        /// <param name="name">Name of the value (in the shader)</param>
        /// <param name="id">Texture ID</param>
        public void SetTexture(string name, int id)
        {
            if (!textureValues.ContainsKey(name)) textureValues.Add(name, id);
            else textureValues[name] = id;
        }

        /// <summary>
        /// Enables the shader associated with the material
        /// and loads all the preferences
        /// </summary>
        public void UseMaterial()
        {
            if (Shader != null)
            {
                Shader.Enable();
                LoadProperties();
            }
        }

        /// <summary>
        /// Loads the material properties to the shader
        /// </summary>
        private void LoadProperties()
        {
            foreach(KeyValuePair<string, Vector4> pair in vec4Values)
            {
                Shader.Set(pair.Key, pair.Value);
            }
            foreach (KeyValuePair<string, Vector3> pair in vec3Values)
            {
                Shader.Set(pair.Key, pair.Value);
            }
            foreach (KeyValuePair<string, Matrix4> pair in mat4Values)
            {
                Shader.Set(pair.Key, pair.Value);
            }
            foreach (KeyValuePair<string, int> pair in intValues)
            {
                Shader.Set(pair.Key, pair.Value);
            }
            foreach (KeyValuePair<string, float> pair in floatValues)
            {
                Shader.Set(pair.Key, pair.Value);
            }

            TextureUnit tUnit = TextureUnit.Texture0;
            int index = 0;
            foreach(KeyValuePair<string, int> pair in textureValues)
            {
                GL.ActiveTexture(tUnit);
                GL.BindTexture(TextureTarget.Texture2D, pair.Value);
                Shader.Set(pair.Key, index);
                tUnit++;
                index++;
            }
        }


    }
}
